package hydrap2p.search.ubuntu;

import hydrap2p.helper.ServiceConnector;
import hydrap2p.helper.ThriftServerThread;
import hydrap2p.helper.ThriftUtils;
import hydrap2p.modulemanager.ModuleControl;
import hydrap2p.servicemanager.NameTaken;
import hydrap2p.search.SearchProvider;
import hydrap2p.search.SearchQuery;
import hydrap2p.search.Searcher;

import java.io.IOException;
import java.net.BindException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;

import org.apache.log4j.PropertyConfigurator;
import org.apache.thrift.TException;

public class UbuntuSearcher implements SearchProvider.Iface, ModuleControl.Iface {

	// Init logger
	static {
		//Configure log4j
		Properties log4jProps = new Properties();
		log4jProps.setProperty("log4j.rootLogger", "TRACE, console");
		log4jProps.setProperty("log4j.appender.console", "org.apache.log4j.ConsoleAppender");
		log4jProps.setProperty("log4j.appender.console.layout", "org.apache.log4j.PatternLayout");
		log4jProps.setProperty("log4j.appender.console.layout.ConversionPattern", "%-4r [%t] %-5p %c %x - %m%n");

		PropertyConfigurator.configure(log4jProps);
	}
	
	private Map<Integer, UbuntuSearchThread> searches;
	private static ThriftServerThread server;
	private static ThriftServerThread servercontrol;
	private static final int SEARCHCONNECTIONGRACE=5000;	// how many milliseconds to wait for a connection to the searcher
	public static final String SEARCHPROVIDERNAME="Ubuntu Releases";
	private static Timer timer;
	private static final long pingerdelay = 10000;
	
	public static void main(String[] args)
	{
		UbuntuSearcher mainclass=new UbuntuSearcher();
		server=null;
		try {
			server=ThriftUtils.initThriftServer(new SearchProvider.Processor(mainclass), "SearchProvider.Ubuntu", 9675);
		} catch (BindException e) {
			System.out.println("Could not bind to a port");
		} catch (IOException e) {
			System.out.println("Could not contact manager");
		} catch (NameTaken e) {
			System.out.println("Ubuntu Search already started");
		}
		try {
			servercontrol=ThriftUtils.initThriftServer(new ModuleControl.Processor(mainclass), "SearchProvider.Ubuntu.Control");
		} catch (BindException e1) {
			System.out.println("Could not bind to a port");
		} catch (IOException e1) {
			System.out.println("Could not contact manager");
		} catch (NameTaken e1) {
			System.out.println("Ubuntu Search Module Control already started");
		}
		if (server!=null && servercontrol!=null)
		{
			server.start();
			servercontrol.start();
			
			Searcher.Iface searcher=null;
			long start=System.currentTimeMillis();
			while (searcher==null && System.currentTimeMillis()<start+SEARCHCONNECTIONGRACE)
			{
				try {
					searcher=ServiceConnector.getSearcher();
					if (searcher!=null)
						searcher.registerSearchProvider(server.getLocation());
					else
						throw new TException();
				} catch (TException e) {
					// could not connect, try again
				}
				if (searcher==null)
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// What causes this?
					}
			}
			if (searcher==null)
			{
				System.out.println("Could not connect to Search Manager");
				mainclass.stop();
			}
		}
		else
		{
			mainclass.stop();
		}
	}
	
	public UbuntuSearcher()
	{
		searches=new TreeMap<Integer, UbuntuSearchThread>();

		timer = new Timer();
		timer.schedule(new PingTimer(), pingerdelay, pingerdelay);
	}
	
	@Override
	public String getName() throws TException {
		return SEARCHPROVIDERNAME;
	}

	@Override
	public void startSearch(int arg0, SearchQuery arg1) throws TException {
		synchronized(searches)
		{
			UbuntuSearchThread searcher=new UbuntuSearchThread(arg0, arg1);
			searches.put(arg0, searcher);
			new Thread(searcher).start();
		}
	}

	@Override
	public void stopSearch(int arg0) throws TException {
		UbuntuSearchThread thread=searches.get(arg0);
		if (thread!=null)
			thread.shutdown();
		searches.remove(arg0);
	}

	@Override
	public void ping() throws TException {
	}

	@Override
	public List<String> supportedInterfaces() throws TException {
		return Arrays.asList("Service","SearchProvider","ModuleControl");
	}

	@Override
	public void stop() {
		try {
			Searcher.Iface searcher=ServiceConnector.getSearcher();
			if (searcher!=null)
				searcher.unregisterSearchProvider(server.getLocation());
		}
		catch (TException e)
		{
			// Failed to disconnect from Searcher, which means he doesn't exist, which means we don't need to disconnect from Searcher
		}
		if (server!=null)
			server.shutdown();
		if (servercontrol!=null)
			servercontrol.shutdown();
		if (timer!=null)
			timer.cancel();
		System.exit(0);
	}


	private class PingTimer extends TimerTask {
		@Override
		public void run() {
			try {
				ServiceConnector.getServiceManager().ping();
				ServiceConnector.getSearcher().ping();
				return;
			} catch (Exception e) {
			}
			stop();
		}
	}
}
